import type { Request as IRequest, Response as IResponse } from 'express'
import { DependencyContainer } from 'tsyringe'
import { UserAgent } from "./UserAgent";
import { ENV, INextFunction, NextFunction, ROOT, Request, Response } from './tokens'
import { dirname, join } from 'path';
import { skerReq } from './module';
import { existsSync } from 'fs-extra';
import { getFilePath, parseScope } from './parse';
import { md5 } from './md5';

export class Router {
    originalUrl: string;
    hostname: string;
    path: string;
    headers: object;
    params: object;
    query: object;
    body: object;
    method: string;
    isMobile: boolean;
    // 模块名
    addon: string;
    // 作者空间
    scope: string;
    // 控制器
    controller: string;
    // 执行器
    action: string;
    // do
    d: string;
    // 版本号
    version: string;
    root: string;
    /**
     * 平台
     */
    platform: string;

    res: IResponse;
    injector: DependencyContainer
    env: any;
    req: IRequest;
    next: INextFunction;
    constructor(
        injector: DependencyContainer,
    ) {
        this.injector = injector;
        this.env = this.injector.resolve(ENV)
        const res = this.injector.resolve(Response)
        const req = this.injector.resolve(Request)
        this.req = req;
        this.injector = injector;
        this.res = res;
        const { originalUrl, params, query, body, headers, path, hostname } = req;
        const url = new URL(originalUrl, 'http://localhost')
        this.root = this.injector.resolve(ROOT);
        this.originalUrl = originalUrl;
        this.hostname = hostname;
        this.headers = headers;
        this.params = params;
        this.query = query;
        this.body = body;
        this.path = url.pathname;
        this.method = req.method.toLocaleLowerCase();
        const ua = new UserAgent(req);
        Reflect.set(req, 'ua', ua);
        this.injector.register(UserAgent, { useValue: ua })
        this.isMobile = ua.isMobile;
        this.parse();
        this.next = this.injector.resolve(NextFunction)
    }

    public parse() {
        const paths = this.path.split('/')
        const [start, ...others] = paths;
        const { scope, addon, platform, controller, action, d } = parseScope(others, this.headers, this.query);
        const cacheKey = md5(JSON.stringify({ scope, addon, platform, controller, action, method: this.method, query: this.query, d }))
        this.injector.register('REQ_PATH_HASH', { useValue: cacheKey })
        this.scope = scope;
        this.addon = addon;
        this.platform = platform;
        this.controller = controller;
        this.action = action;
        this.d = d;
    }

    private async checkPermission(): Promise<boolean> {
        // throw new ForbiddenError(`${this.method}.${this.scope}.${this.addon}.${platform}.${this.controller}.${this.action}`)
        return true;
    }

    async load() {
        try {
            const hasPermisson = await this.checkPermission();
            if (!hasPermisson && this.req.accepts('html')) {
                this.res.redirect('/fj/core/web/login');
                return;
            }
            if (!this.scope || !this.addon) {
                return this.next()
            }
            const filePath = this.getFilePath()
            if (existsSync(filePath)) {
                let pkg = skerReq([dirname(filePath)], this.injector)(filePath)
                if (typeof pkg === 'undefined') {
                    return this.next();
                }
                const method = Reflect.get(pkg, this.d)
                if (method) {
                    if (typeof method === 'function') {
                        return method(this.injector)
                    } else {
                        pkg = method;
                    }
                }
                const action = Reflect.get(pkg, this.action)
                if (action) {
                    if (typeof action === 'function') {
                        return action(this.injector)
                    } else {
                        pkg = action;
                    }
                }
                const controller = Reflect.get(pkg, this.controller)
                if (controller) {
                    if (typeof controller === 'function') {
                        return controller(this.injector)
                    } else {
                        pkg = controller;
                    }
                }
                if (Reflect.has(pkg, this.method)) {
                    return pkg[this.method](this.injector)
                }
                if (Reflect.has(pkg, 'default')) {
                    return pkg.default(this.injector)
                }
                return this.next()
            } else {
                console.log(`${filePath} not found`)
                return this.next()
            }
        } catch (e) {
            throw e;
        }
    }

    private getFilePath() {
        return getFilePath({
            root: this.root,
            scope: this.scope,
            addon: this.addon,
            platform: this.platform,
            controller: this.controller,
            action: this.action,
            d: this.d,
            method: this.method
        })
    }

    toJson() {
        return {
            originalUrl: this.originalUrl,
            hostname: this.hostname,
            path: this.path,
            headers: this.headers,
            params: this.params,
            query: this.query,
            body: this.body,
            method: this.method,
            isMobile: this.isMobile,
            addon: this.addon,
            scope: this.scope,
            controller: this.controller,
            action: this.action,
            platform: this.platform,
            d: this.d
        }
    }

    toString() {
        return JSON.stringify(this.toJson(), null, 2)
    }
}
